package com.small.nacos.config.server.controller;

import small.common.http.constants.HttpConstants;

import small.common.core.io.common.FileTypeEnum;
import small.common.core.io.util.FileIoUtil;
import small.common.crypto.util.KeyUtil;
import com.small.nacos.api.config.common.ConfigConstants;
import com.small.nacos.config.server.model.CacheItem;
import com.small.nacos.config.server.service.ConfigCacheService;
import com.small.nacos.config.server.service.LongPollingService;
import com.small.nacos.config.server.utils.DiskUtil;
import com.small.nacos.config.server.utils.MD5Util;
import com.small.nacos.config.server.utils.ResponseUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.channels.Channels;
import java.util.List;
import java.util.Map;

/**
 * @Author zhoujin
 * @Date 2022/11/22 17:41
 */
@Service
public class ConfigServletInner {

    protected static final Logger LOGGER = LoggerFactory.getLogger(ConfigServletInner.class);

    @Autowired
    private LongPollingService longPollingService;

    //获取锁重试次数
    private static final int TRY_GET_LOCK_TIMES = 9;

    public String doGetConfig(HttpServletRequest request, HttpServletResponse response, String dataId, String group,
                              String tenant, String clientIp) throws IOException, ServletException {

        final String groupKey = KeyUtil.getKey(dataId, group, tenant);
        //获取配置锁
        int lockResult = tryConfigReadLock(groupKey);

        if (lockResult > 0) {
            FileInputStream fis = null;
            File file;
            String md5;
            try {
                CacheItem cacheItem = ConfigCacheService.getContentCache(groupKey);
                md5 = cacheItem.getMd5();
                //获取配置文件类型
                final String configType =
                        (null != cacheItem.getType()) ? cacheItem.getType() : FileTypeEnum.TEXT.getFileType();
                response.setHeader(ConfigConstants.CONFIG_TYPE, configType);
                FileTypeEnum fileTypeEnum = FileTypeEnum.getFileTypeEnumByFileExtensionOrFileType(configType);
                String contentTypeHeader = fileTypeEnum.getContentType();
                response.setHeader(HttpConstants.CONTENT_TYPE, contentTypeHeader);

                //获取配置文件
                file = DiskUtil.targetFile(dataId, group, tenant);
                if (fileNotExist(file)) {
                    response.setStatus(HttpServletResponse.SC_NOT_FOUND);
                    response.getWriter().println("config data not exist");
                    return HttpServletResponse.SC_NOT_FOUND + "";
                }
            } finally {
                //释放锁
                ConfigCacheService.releaseReadLock(groupKey);
                FileIoUtil.closeQuietly(fis);
            }
            //返回配置文件内容
            response.setHeader(ConfigConstants.CONTENT_MD5, md5);
            fis = new FileInputStream(file);
            response.setDateHeader("Last-Modified", file.lastModified());
            fis.getChannel()
                    .transferTo(0L, fis.getChannel().size(), Channels.newChannel(response.getOutputStream()));
            //没有找到对应配置类
        } else if (lockResult == 0) {
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            response.getWriter().println("config data not exist");
            return HttpServletResponse.SC_NOT_FOUND + "";

            //未获取到锁
        } else {
            response.setStatus(HttpServletResponse.SC_CONFLICT);
            response.getWriter().println("requested file is being modified, please try later.");
            return HttpServletResponse.SC_CONFLICT + "";
        }

        return HttpServletResponse.SC_OK + "";
    }


    public String doPollingConfig(HttpServletRequest request, HttpServletResponse response,
                                  Map<String, String> clientMd5Map, int probeRequestSize) throws IOException {

        //判断是否支持长轮询
        if (LongPollingService.isSupportLongPolling(request)) {
            longPollingService.addLongPollingClient(request, response, clientMd5Map, probeRequestSize);
            return HttpServletResponse.SC_OK + "";
        }
        //不支持长轮询立即比较md5返回相应
        List<String> changedGroups = MD5Util.compareMd5(clientMd5Map);
        String newResult = MD5Util.compareMd5ResultString(changedGroups);
        response.addHeader(ConfigConstants.PROBE_MODIFY_RESPONSE_NEW, newResult);
        //设置禁用缓存
        ResponseUtil.disableCache(response);
        response.setStatus(HttpServletResponse.SC_OK);
        return HttpServletResponse.SC_OK + "";
    }

    private int tryConfigReadLock(String groupKey) {

        int lockResult = -1;

        for (int i = TRY_GET_LOCK_TIMES; i >= 0; i--) {
            lockResult = ConfigCacheService.tryReadLock(groupKey);

            //未获取到配置类
            if (lockResult == 0) {
                break;
            }

            //获取到了锁
            if (lockResult == 1) {
                break;
            }
            // 重新尝试获取锁
            if (i > 0) {
                try {
                    Thread.sleep(1);
                } catch (Exception e) {
                    LOGGER.error("An Exception occurred while thread sleep", e);
                }
            }
        }

        return lockResult;
    }

    private static boolean fileNotExist(File file) {
        return file == null || !file.exists();
    }
}
